Manual Unpacking #4

 

 :: Информация ::

Pulse Reverseing Force

Програма: notepad.exe
Tрудност: 2/10
Инструменти: Ollydbg
Разлика при кракването:Просто не е кракване а ръчно разопаковане! Ще трябва да преодолеем anti-debugging,anti-tracing кодове и т.н.(exe-то е пакнато с всички опции на пакера)

::: Съдържание ::

В този туториал ще се запознаем с един друг криптатор,не толкова известен,но според мене заслужаващ нашето внимание.Ще разопаковаме(unpack-нем) едно notepad.exe криптирано с Yoda's Crypter v1.0.Но този път няма да се занимаем само с разопаковане а ще навлезем по-навътре и ще разберем как работи криптатора на Yado.Това което обикновенно се нарича revers-ване.А именно да разбереш как в действителност работи дадена програма.

Както споменах по-горе при разопаковане на Yoda's Crypter v1.0 ще трябва да се сборим с малко анти-tracing,debugging код.

В миналите ми туториали за MUP съм забравил да дам основните неща който трябва човек да спазва при разопаковане на дадено еxe.Затова сега разгледайте следните стъпки:

1.Прекъсване(спиране) на входната точка на програмата(PEP-Program Entry Point).

2.Преодоляване на защитния код(ако има такъв).

3.Достигане до оригиналната входна точка.

4.Записване на разопакованото вече в паметта exe на твърдия диск.

5.Поправяне оригиналната входна точка на записаното exe.

6.И ако пакера поврежда Import Table,IAT,Bound Imports или TLSTable e нужно да се поправят.(С нашия пример това няма да ни се наложи,само RVA и размера на Import Table ще променим)

Първата стъпка можете да я изпълните по много начини.Когато имате само Softice,HexEditor и евентуално някои Pe Editor,можете да използвате метода на ръчно прекъсване на int3(Int3manual).Ако имате LordPe и Softice можете да използвате същия метод,но става много по-лесно(bpint3 в Softice и с дясното копче на мишката давате нафайла=>Break'n'Enter).Спирането на PEP можете да изпълните и като използвате SymbolLoader-a.Но при нас този проблем е решен още като стартираме notepad.exe с Ollydbg,защото Olly спира изпълнението на програмата на входната точка на пакера/програмата(PEP).Заредете в OllyDbg програмата notepad.exe и трябва да видите следното:

yC:0040D060 pusha  //запазва всички регистри в стека                                      
yC:0040D061 call $(еip)+5 //вика следващия ред                                           |      --- \  
yC:0040D066 pop ebp  //ebp:=40D066                                                   |            -->взима "Delta Offset"
yC:0040D067 sub ebp, (offset loc_401AE4+3) //изважда 40D066-401AE7    |     --- /    
yC:0040D06D call sub_40D113 // \
yC:0040D072 call sub_40D148 //   --->разяснени са по-долу
yC:0040D077 call sub_40D201 //  /

Първия ред запазва всички регистри в стека,за да може след процеса на раопаковане на програмата да ги възстанови с POPAD.Вижте следващите три реда :

yC:0040D061 call $+5
yC:0040D066 pop ebp
yC:0040D067 sub ebp, (offset loc_401AE4+3)

Какво представлява "Delta Offset"?

Те взимат така нареченото делата разстояние(delta offset).За да можете да го разберете,първо трябва да обясня какво представлява delta offset.Използван е един много удобен начин за избягване използването на Base Relocation(основно пренасочване-когато файлът не се стартира на подразбиращия се адрес(Image Base)).Delta Offset e разликата между адреса на който трябва да бъде заредено exe-то в RAМ-а и адреса в RAM-a на който то се зарежда в действителност.За да се избегне използването на Base Relocation просто се взима тази разлика и се записва в някои регистър.После,когато искаме да използваме определен ресурс на някакво място в RAM-a ние няма да използваме само зададените оригинални адреси на който трябва да е дадената информация,а за да намерим тази информация ще трябва да съберем тази разлика Delta Offset с адреса посочен по подразбиране.По този начин ние ще попадаме винаги на точното място,когато искаме да вземем информация(или под форма на ресурси) заредена в RAM-а.За да ви стане по-ясно разгледайте следното:

yC:0040D0FB add eax, dword ptr ss:(loc_401F12+2)[ebp]
yC:0040D101 mov ebx, offset loc_401B8D

Това е код от пакнатия notepad,само че малко по-надолу.Забелязвате,че отстрани  на адресите има [ebp].Това означава,че за да намерим вярното място,на което е търсената информация трябва към подразбиращия се адрес(401F18;401F14) да прибавим и стойността на ebp.Този начин на пренасочване е много ефективен.Използва се най-вече при вируси и при лоадери,защото най вече тогава се налага пренасочване.Обикновенно всяко нормално exe се зарейда на неговия по подразбиране адрес(Image Base),най-често 400000.Сега възниква въпроса как така всички exe-та се зареждат на тяхния по подразбиране адрес след като е почти невъзможно да се улучи точно адреса 400000?Отговорът е съвсем логичен и се съдържа в понятиято виртуална памет.Лоадера на windows създава при всяко стартиране на файл нов блок виртуална памет.И в крайна сметка се получават много такива блокове като всеки има адрес 400000,който не е зает.В паметта всяко exe си има тделен блок от жиртуална памет,така че обикновенно няма никаквозначение колко пъти ще бъде стартирана дадена програма защото тя не зависи от другите.

Kак работи този метод?:

Ето и основния принцип на работа ан този метод:

    Local GetIpCall                                                         // ---\        

    call GetIpCall                                                           // -------->Register-някой регистер:ebp,ebx,edx...

GetIpCall:                                                                    //----/

    pop Register                                                                                                  

    sub Register,offset GetIpCall                                                                                

 

Call-ът на адрес 0040D061 извиква следващата функция(eip+5) като запомня адреса,на следващата инструкция в стека.Следващия ред възстановява стойността в регистър ebp.Стойността на ebp е 40D066

Ebp съдържа стойността  40D066,на който трябва да се зареди exe-то.Изважда се стойността по подразбиране с тази на която е зареден в действителност файлът и така се получава Delta Offset-а.

Виждате трите поредни call-a?Нека да разгледаме първият от тях:0040D06D call sub_40D113.Ако влезете в него ще видите че той взима основните адреси(Base Address e с Image Base) на Api-тата LoadLibraryA и GetProcAddress,като взима Image Base отива на offset 3C(e_lfanew) от там разбира къде започва Pe Header и после прибавя 80 за да намери от кой адрес започва Import Table.След това намира в RAM-a адресите на Loadlibrary и GetProcAddress.

Вторият 0040D072 call sub_40D148 взима адресите не други API функции.За да разберем на кои по точно е най-добре да влезем в call-а с F7 и да проследим какво прави програмата.Като влезете в този call ще видите как последователно се взимат адресите на "VirtualProtect" , "GetModuleFileNameA" , "CreateFileA" , "GlobalAlloc" , "GlobalFree" , "ReadFile" , "GetFileSize" и "CloseHandle".Всички тези функции са от kernel32.dll.Тези адреси се запазват за да бъдат използвани по-късно.Ако влезете в call-а ще видите че първо сe взима адреса на kernel32.dll и после се запаметява в esi.Ако влезете и в под-call-овете ще видите,че всички адреси се взимат с GetProcAddress като преди това има два push-a,където се задават параметрите на функцията GetProcaddress(първо се задава търсеното API,а после и адреса на DLL-a в който се намира).Всички адреси на Api-тата се запазват на различно място в паметта(ebp+някаква_стойност),за да могат по-късно да бъдат използвани.Ето и за улеснение съм взел информация за функцията GetProcAddress от win32.hlp:

The GetProcAddress function returns the address of the specified exported dynamic-link library (DLL) function. 

FARPROC GetProcAddress(

HMODULE hModule, // handle to DLL module 
LPCSTR lpProcName // name of function 
);

Return Value

If the function succeeds, the return value is the address of the DLL's exported function.
If the function fails, the return value is NULL. To get extended error information, call GetLastError.

Надявам се,че разбирате какво прави функцията,но за по сигурно ще обясня на български.Функцията GetProcAddress връща адреса на другата функця,която се търси и името и е запазено в стека.Нормално GetProcAddress връща адреса на функцията в рама,но ако се провали се връща нула.По този начин програмата взима адресите на всички необходими функции,който ще се използват после.

      Забележка:Всички параметри на API функции се запаметяват в стека в обратен ред,но там(в стека) те са подредени точно така както ги виждате в Win32.hlp.

В следващия call:0040D077 call sub_40D201 се вика API-функцията VirtualProtect  за да защити(предвиди) ново допълнително място(страници) в паметта за след разкриптирането.Тоест разширява SIzeOfImage за да няма проблеми при изпълнение на декриптираното exe.

yC:0040D07C test ss:(off_401F25+3)[ebp], 8
yC:0040D086 jz short loc_40D093
yC:0040D088 call sub_40D35F
yC:0040D08D mov dword ptr ss:(loc_401F29+3)[ebp], eax

 На реда 0040D07C test ss:(off_401F25+3)[ebp], 8 се сравнява стойноста на адрес 401F28+ebp с 8 и ако са равни скача на адрес 40D35F.Всъщност тези два реда проверяват дали сме пакнали exe-то с CRC опцията и ако сме го направили, функцията не скача на адрес 40D35F,а изпълняма call-a на адрес 40D088.Toзи call отваря файлът и му изчислява CRC-то.Ако искате да пропуснете този call може просто да преустановите флага за нулевия резултат когато сте още на адрес 40D086,което ще накара програмата да продължи изпълнението си от адрес 40D093 и ще прескочи call-a и следващата функция(след call-a)която запазва стойността върната от този call.Ако искате просто да преодолеете тази защита преустановете флага,но нека да види как точно се изчислява CRC-то на този бинарен файл.

Първо би трябвало да кажа като за начало какво точно представлява CRC-то на даден файл.CRC означава в действителност Cyclic Redundancy Code.Това е стойност(уникална само за този файл),която служи при проверката дали файлът e идентичен.Може би ще се запитате за какво ни трябва да проверяваме дали файлът е идентичен.Ами отговорът не е чак толкова далеч.Понякога CRC-то служи като защитно средство срещу cracker-и.Когато кракваме дадена програма ние малко или много променяме нейния код и съответно нейното CRC.Даже и да не добавяме допълнителен код на програмата а само като променяме няколко байта(даречем je->jne,jmp->nop,jge->jnge и т.н.) понеже всеки файл е е еди дълъг niz от битове.(всеки файл е в формата на дълъг низ от нули и единици,но за по-лесно ние с Hex Editor го вждаме под формата на 16-десетичен niz)Даречем като променим JE-JNE ние променяме фактически HEX кода от 74 на 75 ние променяме 1110100 на 1110101.Следователно макар е с един бит ние променяме целия стринг(няма значение че не променяме дължината на бинарния  низ(файлът)).Когато се пресмята CRC-то на кракнатия файл(променили сме фактически един бит 0 на 1) и се сравнява с CRC-то което е пресметнато когато файлът е компилиран(тоест оригиналния файл) и има вероятност да се засече разликата.Защо казвам има вероятност?Следователно може нашия байт 1 да премине през CRC проверката незасечен.За да разберем дали ще се засече нашия променен бит трябва да изпълним целия процес на изчисляване на CRC и да видим дали изчисленото CRC съвпада с оригиналното.За целта ще трябва да изучим начина по който се изчислява CRC-то на всеки файл.

       Забележка:Не при всеки файл се изчислява CRC.Когато видите с някой Pe Editor полето CheckSum да е 00000000,а дори и то да де е нули,когато не се сравнява с оригиналното можете да не се безпокойте за CRC проверка,защото такава няма да има....Това е разбира се въпрос на програмиста,дали ще сложи тази опция при създаването на EXE-то!При повечето файлове не се слага тази опция.....Поне така съм забелязал!При архивирането на файл с winace или winrar се изчислява почти винаги CRC....

 Ако имате желание потърсете в нета за есета,който обесняват техниката на изчисляване на CRC.Аз ще се опитам да ви обесня нещата така както аз ги знам.Както казах,главната идея на CRC проверката е да разглежда файлът като един дълъг низ от битове(затова се наричат и бинарни файлове).Но какво правим след като разгледаме даден файл като дълъг низ от битове?Как трябва да изчислим CRC-то?За целта няма да взимаме истински файл(тоест нормален файл който е прекалено дълъг за обучителна цел),а ще работим с един низ не по дълъг от 32 бита.Първо трябва да спомена най-общо принципа на намиране на CRC.Изчисляването на CRC представлява последователно делене на нашия бинарен стринг(файл) с някакъв ключ,като накрая ще получим CRC-то на дадения файл.Но има някои особености при деленето на този низ,които ще разгледаме след малко.Даречем имаме файл с дулжина 64 кб и в бинарна форма той изглежда така:

10011011010010100101101000110110

 Как можем да намерим CRC-то на този файл?За целта трябва да си изберем ключ с който да пресметнем CRC-то.Този ключ даречем ще бъде:110001(можем съвсем свободно да си избираме този ключ).

Значи...нека да обобщим още веднъж!За целта трябва да разделим нашия примерен файл на нашия свободно избран ключ,като после ще получим евентуално остатък,който ще е нашето CRC(като казвам евентуално имам впредвид,че CRC-то може и да е нула-да няма остатък).Но при пресмятането на CRC се използва "специялна" аритметика(специялен начин на изваждане и събиране).Всъщтност деленето представлява Х пъти изваждане на нашия ключ от стринга от битове(файл).

Нека като за начало да намерим CRC-тo по нормалните закони на математиката.Трябва да изваждаме ключът 110001 от бинарния низ докато получим остатъка.

При деленето на низа 0011011010010100101101000110110 с ключа 110001 ще се получи остатък 10110.Ако искате можете да го изпробвате като в калкулатора на windows използвате инструкцията MOD-дава остатъка при делението на две числа.

 

Сега да видим как ще изглежда остатъка като приложим CRC аритметиката.Понеже при пресмятане на CRC се започва винаги от най-десния бит "1" Поради тази причина CRC-то,което ще получим с този ключ(110001) ще бъде с един бит по-малък от ключът.Фактически на края винаги се получава следното:прилага се изключващо или(XOR) на остатъка от последното "Изваждане"(без преноса,защото това е по-особено изваждане) и ключа.При последното изваждане се получава винаги:  

1***** (остатъка при последното изваждане)       \

     xor                                                                           ->  *****(CRC)

1***** (нашия ключ)                                           /

Виждате от по горната схема,че ако пресмятаме CRC с ключ дълъг 6 бита(1*****) ще получим CRC дълго 5 бита(*****).Така когато изчисляваме CRC с ключ дълъг n-бита на брой ще получаваме винаги CRC с дължина n-1 бита на брой.Сега възниква въпросът:Ако имаме произволен файл най-вероятно е при последното изваждане да не останат точно n на брой бита които са равни на дължината на ключа.Тоест при деленето могат да останат неразделени битове.Поради тази причина,за да могат всички битове от файла да бъдат разделени се прибавят низ от нули който е дълъг с едно по малко от ключа.Разгледайте нашия пример по долу и как се извършва деленето.Ако искате за да се уверите можете да проверите че ако не добавим допълнителните нулеви битове няма да можем да разделим докрай низа. 

файл:0011011010010100101101000110110                                                                        / --- нулеви битове

ключ:110001 ->дължината на този ключ е 5(а не 6)                                                       /

файл след прибавянена нулеви битове:0011011010010100101101000110110|00000  <--

001101101001010010110100011011000000

     110001|    |   |    |  |    |  |  |          |        

     --------v   |   |    |  |    |  |  |          |

     000111100|   |    |  |    |  |  |          |  

       -  110001|   |    |  |    |  |  |          |    

          --------v   |    |  |    |  |  |          |

          00110110 |    |  |    |  |  |          |

          -   110001|    |   |   |   |  |         |

            ----------v    |   |   |   |  |          |

              000111100|   |   |   |  |          |

             -     110001|   |   |   |  |          |

              ------------v  |   |   |   |         |

                   00110110|   |   |   |         |

              -       110001|    |   |  |          |

                  ------------v    |  |  |           |

                      000111110|   |  |          |

                  -        110001|   |  |          |

                   ---------------v   |  |         |

                           00111110|   |         |

                        -      110001|  |          |

                           ------------v  |          |

                               00111100|          |

                           -      000000 |         | 

                            -------------v         |

                                  000000110110|

                                             110001|

                                        ------------v

                                             000111000

                                                  110001

                                         ------------------

                                                  00100100  ---->CRC(остатък)

                                                  

  Както виждате CRC-то на този низ от битове(файл) е 100100.Принципът на изчисляване на CRC и на други бинарни файлове е същият.Може единствено да има разлика при самия ключ на който се дели и естествено файла.Тази технология на изваждане наподобява инструкцията XOR.Когато се изважда един бит от друг и когато има пренос той(преносът) се игнорира.Както забелязвате при всяко следващо изваждане се се започва винаги от най-крайния десен бит единица и се взимат толкова битове колкото трябват за да стане низа равен на ключа и да може да се извърши изваждането.В този случай остатъка е 100100 което е и CRC-то на този низ.Вижте следващата схема която пояснява начина на изваждане при този вид аритметика-CRC аритметиката.

 

  0-0=0                \         ---->По принцип 0-1=-1,но понеже не се счита флага за пренос числото е положително.

  0-1=1     ===      \ ---/    Начина по който се изваждат битове при CRC аритметиката      

  1-0=1     ===      /             Не се взима под внимание флага за пренос

  1-1=0                /   

 

 

  0+0=0              \

  0+1=1    ===     \               Така се събират числа по CRC аритметиката.Както забелязвате при 1+1 

  1+0=1    ===     /               не се зачита флага за пренос.

  1+1=0              /->няма пренос

 

Всъщтност това си е най обикновенна аритметика само че флага за пренос не се взима в предвид.След като научихме как да изчисляваме CRC-то възниква въпросът как да си избираме ключа с който ще изчисляваме CRC.Предполагам сте чували за CRC-16 или CRC-32?Те са най-разпространените видове CRC проверки.При тези два вида ключът е или 17 битов или 33,за да се получи накрая 16 или 32 битов остатък CRC.След като възниква въпроса за избиране на ключ това означава че различните ключове ще имат различна продуктивност.Тоест един ключ ще е по добур от друг в засичането на грешки.Затова преди нас,по-мъдри хора са изследвали различните вариянти на ключове при 16 и 32 битови CRC проверки.

Ето и най-разпространените ключове който се използват при CRC-16 и CRC-32:10001000000100001 и 100000100110000010001110110110111.

Надявам се да сте добили представа какво представлява CRC и как се пресмята.За по подробно обяснение можете дапотърсите есета на англииски в www.google.com.След като смесе запознали най-общо с изчисляването на CRC е време да се върнем към разопаковането на notepad.exe и преодоляването на CRC защитата. 

   Както обясних по рано за да прескочим CRC проверката ще преустановим нулевия флаг,когато сме на адрес 0040D086 и ще прескочим процедурата за изчисляване на CRC.Но нека да видим какво става действително ако не прескочим call-a за пресмятане на CRC.Влезте в кала с F7 и трябва да видите следния код:

yC:0040D35F push 104h //размера на буфера,където ще се запише пътеката на файла
yC:0040D364 lea edi, (off_401FC5+2)[ebp] //edi сочи буфер от паметта,където ще се копира пълната пътека на файла
                                                                  Например:c:\notepad.exe.

yC:0040D36A push edi //edi,съдържащ параметъра lpFilename се съхранява се съхранява 
yC:0040D36B push 0 //=>GetModuleFileNameA ще върне пътеката на файла,използван да се създаде извикващия процес
yC:0040D36D call dword ptr ss:(loc_401F62+1)[ebp]  //извиква API функцията GetModuleFileNameA,която връща дължината на пътеката на файла в EAX.След функцията GetModuleFileHandle,буфера сочен от edi по рано ще е изпълнен с пълната патека на файла:c:\.......\notepad.exe 

yC:0040D373 push 0 //този параметър е игнориран
yC:0040D375 push 80h //FILE_ATTRIBUTE_NORMAL=80,този параметър също е пренебрегнат 
yC:0040D37A push 3 //OPEN_EXISTING=3,функцията отваря файла
yC:0040D37C push 0 //=>във файла няма директория за сигурност
yC:0040D37E push 1 //FILE_SHARE_READ=1 => файлът е с достъп само за четене
yC:0040D380 push 80000000h //dwDisiredAccsess=Generic_Read=80000000
yC:0040D385 push edi //edi:=пътеката на файла
yC:0040D386 call dword ptr ss:loc_401F73[ebp] //извиква API функцията CreateFileA за да отвори файла.Понеже CreateFileA не създава нов фаил,а отваря съществуващ,тя игнорира някои параметри dwFlagsAndAttributes,hTemplateFile и lpSecurityDescriptor,ако lpSecurityAttributes не е нула
yC:0040D38C cmp eax, 0FFFFFFFFh // има ли грешка при изпълнението на;eax:=0000001C функцията(0FFFFFFFFh=-1=INVALID_HANDLE_VALUE)
yC:0040D38F jnz short loc_40D395 //ако няма скочи
yC:0040D391 xor eax, eax //иначе eax:=0
yC:0040D393 jmp short locret_40D3E1 //и излез от функцията

 Чрез API функцията GetModuleFileNameA се взима пътеката на файла,за да може той после да се отвори с API-то CreateFileA.Функцията CreateFileA връща отворения handle на файла,като той в случая е 0x0000001Ch.Информацията за различните API-функции на Windows съм взел от win32.hlp-можете да го намерите из нета,а различните константите от включения файл заедно с макро асемблер windows.inc.Естествено има и много други помощтни файлове за обяснение на API-функциите на windows.Нека да продължим с анализа на кода.

yC:0040D395 mov edi, eax //edi:=handle-a на файла(1C)
yC:0040D397 push 0   //lpFileSizeHigh=0,този параметър не е изискван
yC:0040D399 push edi //handle-a на отворения файл,на който функцията трябва да вземе размера
yC:0040D39A call ss:dword_401FAF[ebp]  //вика GetFileSize,каято връща размера на файла в байтове
yC:0040D3A0 sub eax, 5 //от върнатата стойност в eax,която е размера на файла в байтове се изважда 5
yC:0040D3A3 xchg eax, esi //eax=esi и esi=eax,еаx и esi си разменят стойностите(esi=400000,eax=0000D542)
yC:0040D3A4 push esi //esi=размера на файла(D542)=брой на байтовете,които трябва да се резервират в паметта
yC:0040D3A5 push 40h  //uFlags=GMEM_ZEROINIT =40 
yC:0040D3A7 call dword ptr ss:(loc_401F82+1)[ebp] //извиква GlobalAlloc за да резервира определения брой байтове и да върне handle-a на новия резервиран блок в паматта 
yC:0040D3AD or eax, eax //eax=0?
yC:0040D3AF jnz short loc_40D3B3 // провалила ли се е функцията,ако не е скочи
yC:0040D3B1 jmp short loc_40D3D8 //иначе скача на адрес 40D3D8 и вика функцията CloseHandle за да затвори handle-a върнат от CreateFileA и излиза от call-a

Надявам се по-горните кометари да са ви ясни,но ако нещо не разбирате можете допълнително самостоятелно да потърсите обясненията в Win32.hlp за Api-тата на windows.Все пак нека да обобщим какво точно става в по-горния код.След като вече е взета пътеката на файла и той е отворен с CreateFileA и му е взет новия handle идва ред да се вземе неговия размер,който ще ни трябва при изчисляването на CRC и да се резервира блок(място) от паметта пак за пресмятането на CRC.Но в случай че функцията GlobalAlloc се провали се вика CloseHandle,която затваря Handle-a на отворения файл от CreateFIleA за да се възстанови всичко по старому,все едно не е отварян файла.

yC:0040D3B3 xchg eax, ebx  //eax и ebx си разменят стойностите,като вече ebx съдържа handle-a на резервирания блок памет от GlobalAlloc
yC:0040D3B4 push 0 
//това е параметъра lpOverlapped,но той е нула и е пренебрегнат
yC:0040D3B6 lea eax, (off_401FC5+2)[ebp] 
//eax придобива адреса,където се съдържа пътеката на файла;eax:= адреса на броя байтове,който трябва да бъдат прочетени от функцията ReadFile
yC:0040D3BC push eax
//адреса на броя байтове,които трябва да бъдат прочетени се запазва в стека
yC:0040D3BD push esi 
//запазва се броя байтове който ще се прочетат
yC:0040D3BE push ebx
// запазва се handle-a на блока от памет,който беше създаден от GlobalAlloc,където ще се записва информацията
yC:0040D3BF push edi 
//запазва в стека handle-a на отворения файл,който трябва да бъде прочетен
yC:0040D3C0 call ss:dword_401F9F[ebp] 
//извиква функцията ReadFile;прочита информацията от файла,започвайки от моятото,което е сочено от файловия Pointer;след като прочете файла функцията връща или 1,или 0(True или False)
yC:0040D3C6 mov eax, ebx 
//eax:= handle-a на резервирания блок памет от GlobalAlloc  
yC:0040D3C8 mov ecx, esi 
//ecx:= размера на файла
yC:0040D3CA call sub_40D3E2 
//тaзи call-функция изчислява CRC-то,като записва резултата в EBX
yC:0040D3CF xchg eax, esi 
//резултата от предишния call се запомня в esi
yC:0040D3D0 push ebx 
//запазва handle-a на блока от паме,който трябва да бъде освободен с GlobalFree
yC:0040D3D1 call dword ptr ss:loc_401F92[ebp] 
//извиква API-функцията GlobalFree
yC:0040D3D7 xchg eax, esi 
//eax:=резултата от call-a на адрес 04D3CA,който е изчисленото CRC
yC:0040D3D8 push eax  //запазва този резултат
yC:0040D3D9 push edi 
// запазва handle-a на файла,който трябвадасе затвори
yC:0040D3DA call ss:dword_401FBF[ebp] 
//извиква API-функцията CloseHandle за да затвори файла,отворен от CreateFile
yC:0040D3E0 pop eax 
//в eax се запазва изчисленото CRC
yC:0040D3E1 retn 
//излиза от call-функцията
От по-горния код разбираме кога точно се изчислява CRC-то,а имено в call-a на адрес 0040D3CA,като преди това вика функцията ReadFile да прочете файла.Нека да видим как става самото изчисляване.Алгоритъма е доста лесен.За целта трябва да влезете в call-a на адрес 0040D3CA и да проследите какво става.

yC:0040D3E2 mov edi, eax //handle-a на блока от памет или мястото където в паметта,където е зареден файла
yC:0040D3E4 xor eax, eax  //eax:=0
yC:0040D3E6 xor ebx, ebx  //ebx:=0
yC:0040D3E8 xor edx, edx  //edx:=0
yC:0040D3EA mov al, [edi]  //премества бай от файла(1,2,3...N байт премества докато не свършат) 
yC:0040D3EC mul edx  //умножава стойността в al с edx и записва резултата в edx-eax
yC:0040D3EE add ebx, eax  //събира взетия байт умножен със стойността в edx с стойността в ebx
yC:0040D3F0 inc edx  //след умножението edx е 0,която сега става 1
yC:0040D3F1 inc edi  //edi сочи следващия байт,който трябва да бъде взет;edi:=edi+1 
yC:0040D3F2 loop loc_40D3EA  //край на файла? ако да продължи ако не скочи
yC:0040D3F4 xchg eax, ebx  //eax=CRC
yC:0040D3F5 retn  //излеза от call-a
Това е алгоритъма по който се изчислява CRC.Той изобщо не е сложен.Надявам се че разбирате какво става и без коментарите.Всъщтност,даже съм изненадан малко и подозирам че това е само примитивна форма на CRC изчисление.Деиствителното пресмятане на CRC става както аз ви го обясних по-нагоре.A сега нека да проследим кода по нататък.От инструкцията retn се излиза от този под-call и се изпълнява после API-функцията GlobalFree,за да се освободи мястото резервирано от GlobalAlloc по-рано и най-накрая се вика CloseHandle,за да се затвори файла,отворен от CreateFile.Най-накрая се излиза от целя call,в който влязахме в началото за да изчислим CRC.След това този ред:

yC:0040D08D mov dword ptr ss:(loc_401F29+3)[ebp], eax

запазва изчисленото CRC на адрес ss:401F2C.Ето и следващите редове:

yC:0040D093 mov eax, dword ptr ss:(loc_401F12+2)[ebp]  //eax:=Image Base
yC:0040D099 mov ebx, 1 
//ebx:=1
yC:0040D09E call sub_40D3F6 
//този call разопакова секциите една по една;за да видим как точно става това ще трябва да влезем в самия call

Ето и кода който трябвада видите като влезете в call-функцията:

yC:0040D3F6 mov edi, eax  //edi:=Image Base
yC:0040D3F8 add edi, [edi+3Ch]  //edi сочи началото на Pe Header-a  
yC:0040D3FB mov esi, edi  //esi сочи началото на Pe Header-a
yC:0040D3FD add esi, 0F8h  //esi сочи началото на Section Header-a
yC:0040D403 xor edx, edx  //edx=0
yC:0040D405 mov dx, [edi+6]  //dx:=броя на секциите,в случая 6
yC:0040D409 dec edx  //edx:=edx-1;отчита броя на секциите,който остават след всяко разопаковане
yC:0040D40A cmp dword ptr [esi], 63727372h 
//?rsrc;това resource секцията ли е?
yC:0040D410 jnz short loc_40D414 
//ако да не скачай
yC:0040D412 jmp short loc_40D482 
//скочи на адрес 40D482
yC:0040D414 cmp dword ptr [esi], 7273722Eh 
//?.rsr;това resource секцията ли е?
yC:0040D41A jnz short loc_40D41E 
//ако да,не скачай
yC:0040D41C jmp short loc_40D482 
/скочи на адрес 40D482
yC:0040D41E cmp dword ptr [esi], 6F6C6572h 
//?relo;това секцията за пренасочване ли е?
yC:0040D424 jnz short loc_40D428 
//ако да,не скачай
yC:0040D426 jmp short loc_40D482 
//скочи на адрес 40D482
yC:0040D428 cmp dword ptr [esi], 6C65722Eh 
//.rel?;това секцията за пренасочване ли е?
yC:0040D42E jnz short loc_40D432 
//ако да,не скачай
yC:0040D430 jmp short loc_40D482 
//скочи на адрес 40D482
yC:0040D432 cmp dword ptr [esi], 4379h 
//?yC;това yC секцията ли е?  
yC:0040D438 jnz short loc_40D43C 
//ако да,не скачай
yC:0040D43A jmp short loc_40D482  
//скочи на адрес 40D482
yC:0040D43C cmp dword ptr [esi], 736C742Eh 
//?.tls;това TLS секцията ли е?
yC:0040D442 jnz short loc_40D446 
//ако да,не скачай
yC:0040D444 jmp short loc_40D482  
//скочи на адрес 40D482
yC:0040D446 cmp dword ptr [esi], 6164652Eh 
//?.eda;това .eda секцията ли е?
yC:0040D44C jnz short loc_40D450 
//ако да,не скачай
yC:0040D44E jmp short loc_40D482 
//скочи на адрес 40D482
yC:0040D450 cmp dword ptr [esi], 6164722Eh 
//?.rda;това .rda секцията ли е?  
yC:0040D456 jnz short loc_40D45A 
//ако да,не скачай
yC:0040D458 jmp short loc_40D482 
//скочи на адрес 40D482
yC:0040D45A cmp dword ptr [esi+14h], 0 
//?rawoffset=0;полето rawoffset в section headеr-a на тази секция нула ли е?
yC:0040D45E jnz short loc_40D462 
//ако да,не скачай
yC:0040D460 jmp short loc_40D482 
//скочи на адрес 40D482
yC:0040D462 cmp dword ptr [esi+10h], 0 
//?rawsize=0;полето rawsize в section header-a на тази секция нула ли е?
yC:0040D466 jnz short loc_40D46A 
//ако да,не скачай
yC:0040D468 jmp short loc_40D482 
//скочи на адрес 40D482
yC:0040D46A pusha 
//запазва всички регистри поради предстоящото разопаковане на секцията
yC:0040D46B mov ecx, [esi+10h] 
//ecx=rawsize
yC:0040D46E or ebx, ebx 
//?ebx=0
yC:0040D470 jnz short loc_40D477 
//ако да,не скачай на адрес 40D477
yC:0040D472 mov esi, [esi+14h] 
//esi=rawoffset
yC:0040D475 jmp short loc_40D47A 
//скочи на адрес 40D47A
yC:0040D477 mov esi, [esi+0Ch] 
//esi:=VirtualOffset
yC:0040D47A add esi, eax  //прибавя към esi Image Base
yC:0040D47C call sub_40D48A  //разопакова секцията
yC:0040D481 popa  //възстановява всички регистри
yC:0040D482 add esi, 28h  //esi сочи следващия section header
yC:0040D485 or edx, edx  //свършили ли са секциите за разопаковане
yC:0040D487 jnz short loc_40D409  //ако не скочи
yC:0040D489 retn  //в противен случай излез
Нека да обобщим какво става в по горния код.Той служи за подготовка за разопаковането на секциите,като за всяка секция се взима информация от нейния section header.В този случай се интересуваме само от rawsize,rawoffset и virtualaddress.Като само се проверява дали полетата rawsize и rawoffset не са празни.Virtualaddress се използва за да се определи от къде да започне разопаковането на секцията-тоест от нейното начало,което се съдържа в полето VirtualAddress.След всяка разопакована секция esi сочи следващия section header,от където ще може да вземе информация за следващата секция.Но не всички секции се разопаковат.Затова в началото се проверява коя секция е наред за разопаковане,и ако тя не съвпада с иманата на тези,който трябва да останат неразопаковани(тоест няма нужда да се разопаковат,защото не са опаковани) тогава програмата пристъпва към самото разопаковане което ще видим как става като влезем в call-a на адрес 0040D47C.

yC:0040D48A mov edi, esi  //edi сочи началото на секцията
yC:0040D48C lodsb byte ptr ds:[esi] //зарежда следващия байт за пазопаковане в al
yC:0040D48D xor al, 79h  //разопакова байта,като извършва побитово изключващо или с 79
yC:0040D48F stosb byte ptr es:[edi]  //записва разопакования бай от al в мястото от паметта сочено от es:[edi]
yC:0040D490 loop loc_40D48C  //?ecx=0;разопаковани ли са всички байтове?,ако не скочи
yC:0040D492 retn  //ако да излез

Видяхте как се разопаковат секциите.Алгоритъмът е възможно най-простия който съм виждал до сега.Просто извършва изключващо  или с 79 със всеки байт.Сега е време да се върнем към главния код.Вече сме разопаковали секциите.Да пристъпим на пред.

yC:0040D0A3 test ss:(off_401F25+3)[ebp], 1  //проверява дали сме задали опцията за проверка за softice при пакетирането
yC:0040D0AD jz short loc_40D0BB 
//ако не е прескача
yC:0040D0AF call sub_40D1E9 
//иначе влиза в call-a,където се извършва BCHK-метод за откриване на Softice
yC:0040D0B4 cmp eax, 1 
//проверява дали е намерен Softice
yC:0040D0B7 jnz short loc_40D0BB 
//ако да не скачай
yC:0040D0B9 popa
//възстановява всички регистри
yC:0040D0BA retn 
//излиза
По горния код проверява дали Softice-опцията е зададена при пакването на exe-то,ако е то програмата използва BCHK метода за да провери дали има активиран softice.За да се запознаем по-добре със този метод ще влезем в call-a на адрес 0040D0AF.

yC:0040D1E9 push ebp  //запазва стойността на ebp в стека
yC:0040D1EA mov ebp, 4243484Bh  //ebp=BCHK
yC:0040D1EF mov ax, 4  //аx=4
yC:0040D1F3 int 3   //изпълнява int3,за да провери за активен Softice(Trap to Debugger)
yC:0040D1F4 pop ebp   //възстановява се стойността на ebp
yC:0040D1F5 cmp al, 4  //?al=4
yC:0040D1F7 jz short loc_40D1FE  //ако да,скача на адрес 40D01FE(Softice не е намерен)    |---------------
yC:0040D1F9 xor eax, eax   //eax=0                                                                                                       |
yC:0040D1FB inc eax  //eax:=eax+1                                                                                                       |
yC:0040D1FC jmp short locret_40D200  //скача на адрес 40D20|-----------                                               | 
yC:0040D1FE xor eax, eax  //eax=0                                 <<--------------|---------------------------------------
yC:0040D200 retn  //излиза от call-a                 <<---------------------------
Това е така наречения BoundsChecker метод за засичане на Softice.При него в регистъра ebp се премества низът BCHK,като се вика int3 при ax=4.Това прекъсване(int3) служи за засичане на дебъгер.За да добиете по-голяма представа можете да прочетете "Ralf Brown Interrupt list".Там са описани повечето прекъсвания,но специялно за това,което е в нашия случай не е дадено описание.Ako al след прекъсването все още е равно на 4 => няма Softice.По този начин може да се засече 

Softice само ако имате опцията BoundsChecker(на Softice) зададена.

03 INT 03 C - CPU-generated - BREAKPOINT

03 INT 03 - Columbia PCs (desktop,VP portables) - ROM DEBUGGER 

03 INT 03 - Realia COBOL - DEBUGGER SUPPORT

03 INT 03 U - Watcom WVIDEO, Watcom WD - OUTPUT DEBUGGING MESSAGE 

03 INT 03 - DTown Utilities - POP UP 

030000 INT 03 - Soft-ICE - BACK DOOR COMMANDS - GET Soft-ICE VERSION 

0301 INT 03 - Soft-ICE - BACK DOOR COMMANDS - ??? 

030900 INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ??? 

030902 INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - POPUP & START A DEBUG SESSION 

030903 INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ??? 

030907 INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ??? 

03090A INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ??? 

03090B INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ??? 

030910 INT 03 - Soft-ICE - BACK DOOR COMMANDS - DISPLAY STRING IN Soft-ICE WINDOW 

030911 INT 03 - Soft-ICE - BACK DOOR COMMANDS - EXECUTE Soft-ICE COMMAND 

030912 INT 03 - Soft-ICE - BACK DOOR COMMANDS - GET BREAKPOINT INFORMATION 

030913 INT 03 - Soft-ICE v2.5x - BACK DOOR COMMANDS - SET Soft-ICE BREAKPOINT 

030914 INT 03 - Soft-ICE v2.5x - BACK DOOR COMMANDS - REMOVE Soft-ICE BREAKPOINT 

030918 INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ??? 

0310 INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ??? 

0311 INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ??? 

03130C INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ??? 

03130E INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ??? 

031313 INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ??? 

030900 INT 03 - Soft-ICE v2.80 - BACK DOOR COMMANDS - ???

 

 

Има много начини за засичане на Softice.Тука е използван само един от тях,което прави пакнатия файл уязвим срещу кракери.Но за да сте запознати и с другите,най-общи начини за засичане на Softice ще се опитам да ги обясня на кратко в този туториал.Трябва да предупредя,че това изобщо не са всички начини за засичане на Softice.Най-вероятно има още много други,който не се използват толкова често.Но вижте какво съм ви предоставил аз:

Методи за засичане на Softice

Метод  01
=========

Този метод се използва още доста често,но е лесен за предотвратяване.Използва 
се за да се вземат така наречените 'Back Door commands' na Softice,което дава
информация за точките на прекъсване в Softice или изпълнява команда на Softice...
Този мтрик се използва съсщо и за'crash'-ване на Softice и го принуждава да
изпълни някаква команда (HBOOT...)  

Ето едно бързо описание:
-AX = 0910h   (показва низ в прозореца на Softice)
-AX = 0911h   (Изпълнява командa на Softice, ds:dx)
-AX = 0912h   (взима информация за точка на прекъсване)
-AX = 0913h   (поставя точка на прекъсване в Softice)
-AX = 0914h   (премахва точка на прекъсване в Softice)

Този трик можете да разпознаете по стойностите на SI и DI:
-SI = 4647h ('FG')
-DI = 4A4Dh ('JM')
4647h и 4A4D са магическите стойности за Softice.Ако искате да разберете повече
е най-добре да видите "Ralf Brown Interrupt list".По-горе съм предоставил 
главата за int3 от листата на Ralf Brown.
Предоставил съм ви примерен код от "Haspinst.exe",което се използва да
защитава досовски програми:

4C19:0095   MOV    AX,0911  ; изпълнява команда.
4C19:0098   MOV    DX,[BX]  ; ds:dx сочи към команда.
4C19:009A   MOV    SI,4647  ; първата магическа стойност.
4C19:009D   MOV    DI,4A4D  ; втората магическа стойност.
4C19:00A0   INT    3        ; извиква прекъсване
4C19:00A1   ADD    BX,02    ; сочи към следващата команда
4C19:00A4   INC    CX
4C19:00A5   CMP    CX,06    ; и 6-те команди ли са изпълнени?
4C19:00A8   JB     0095     ; ако да не скачай.
4C19:00AA   JMP    0002     ; скача(Softice е засечен).
4C19:00AD   MOV    BX,SP    ; Softice не е засечен.

Програмата ще изпълни 6 различни команди на Softice,като командата е сочена от
регистрите ds:dx.Ще се изпълнят следните команди:LDT,IDT,GDT,TSS,RS
и HBOOT.Скокът към 00AD се извършва ако няма засечен дебъгер. 
___________________________________________________________________________


Meтод   3
=========

Towa e po-malko izpolzwan metod.Търси за ID-то на Softice VxD чрез int 2Fh/1684h

        

    xor     di,di
    mov     es,di
    mov     ax, 1684h       
    mov     bx, 0202h       ; VxD ID на winice
    int     2Fh
    mov     ax, es          ; ES:DI -> входната точка на VxD Api
    add     ax, di
    test    ax,ax
    jnz     Softice е засечен

___________________________________________________________________________

Метод   4
=========

Това е метод,който е подобен на предния.Той търси ID-то на Softice
GFX VxD.

    xor     di,di
    mov     es,di
    mov     ax, 1684h       
    mov     bx, 7a5Fh       ; VxD ID на SIWVID
    int     2fh
    mov     ax, es          ; ES:DI -> входната точка на VxD API
    add     ax, di
    test    ax,ax
    jnz     Softice е засечен

__________________________________________________________________________


Метод   5
=========

Това е метод,който търси магическия номер 0F386h,който се връща в AX от всички
системни дебъгери.Вика прекъсване 41,като AX=4f.
There are several alternatives.  

Пример:

    mov     ax,4fh
    int     41h
    cmp     ax, 0F386
    jz      SoftICE_detected


Това е малко по сложен пример,но идеята е една и съща:

    mov     bx, cs
    lea     dx, int41handler2
    xchg    dx, es:[41h*4]
    xchg    bx, es:[41h*4+2]
    mov     ax,4fh
    int     41h
    xchg    dx, es:[41h*4]
    xchg    bx, es:[41h*4+2]
    cmp     ax, 0f386h
    jz      SoftICE_detected

int41handler2 PROC
    iret
int41handler2 ENDP


_________________________________________________________________________


Mетод  06
=========


Ето един по-сложен метод за засичане(той е подобен на предния):


int41handler PROC
    mov     cl,al
    iret
int41handler ENDP


    xor     ax,ax
    mov     es,ax
    mov     bx, cs
    lea     dx, int41handler
    xchg    dx, es:[41h*4]
    xchg    bx, es:[41h*4+2]
    in      al, 40h
    xor     cx,cx
    int     41h
    xchg    dx, es:[41h*4]
    xchg    bx, es:[41h*4+2]
    cmp     cl,al
    jnz     SoftICE e засечен

_________________________________________________________________________

Метод  07
=========

Това е метод,чрез който се засича Handler-a на winice чрез int68h (V86)

    mov     ah,43h
    int     68h
    cmp     ax,0F386h
    jz      SoftICE e засечен


=> не е възможно да сложите BPINT 68 в Softice,но можете да hook-нете 32Bit
   програми:

   BPX 'прекъсване' if ax==68
   (извиканата функция е на адрес byte ptr [ebp+1Dh])
__________________________________________________________________________


Mетод  08
=========

Toва не е метод за засичане на SoftICE,но възможност да се срине системата
чрез изпълнение на int 01h and int 03h и пренасочвайки ги към други процедури

Вика се прекъсване 21,като ax=25/35(сложи/вземи вектор на прекъсване) и ds:dx 
сочи към новия код,който трябва да бъде изпълнен.

    mov     ah, 25h
    mov     al, номер на прекъсване(01h or 03h)
    mov     dx, offset New_int_routine
    int     21h

__________________________________________________________________________

Meтод  09
=========

Този метод е много близък до 3 и 4 мтод(int 2Fh/1684h) но може да се
изпълни само в среда ring0 (VxD или a ring3 апликации използвайки VxdCall).
Get_DDB се използва,за да определи дали е инсталиран VxD за определеното
устройство и връща Device Description Block (в ecx) за това устройство,
ако то е инсталирано.

   mov     eax, ID на устройството; 202h за SICE или 7a5Fh за SIWVID VxD ID
   mov     edi, име на устройството; използва се само ако няма VxD ID 
   VMMCall Get_DDB
   mov     [DDB], ecx       ; ecx=DDB или 0,ако VxD не е инсталиран 

Този метод лесно може да бъде засечен със SoftICE:
   bpx Get_DDB if ax==0202 || ax==7a5fh

__________________________________________________________________________

Метод  10
=========

=>Трябва да премахнете точките на прекъсване при преодоляване на този вид защита
!!!
Ето го и метода!Много е добър:
Този вид защита работи,като проверява регистрите за debug-ване(dr7)
aко сте заредили програмата със Softice,тогава dr7=0x700,а в противен слуай 
dr7=0x400)Или ако има сложени memory breakpoints(BPMs)се прочитат стойностите на
dr0-dr3,но само в среда ring0.Стоностите на регистрите dr0-dr3 могат лесно да
бъдат прочетени или променени:тоест можете да премахвате BPMs.

__________________________________________________________________________

Метод  11
=========

Това е така познатaта 'Meltice' защита.Тя може да се преодолее по много начини.
Като сложите точка на прекъсване на CreateFileA и потърсите за заредените в Ram-a
driver-и на SoftICE.Toзи метод е използван от Symbol Loader-a на Numega за да 
проверява дали Softice e зареден.

Ето и как работи този трик:
Опитва се да отвори driver-ите на Softice(SICE, SIWVID за Win9x, NTICE за
WinNT) с API-Функцията CreateFileA.

Ето и пример как работи това:

BOOL IsSoftIce95Loaded()
{
   HANDLE hFile;  
   hFile = CreateFile( "\\\\.\\SICE", GENERIC_READ | GENERIC_WRITE,
                      FILE_SHARE_READ | FILE_SHARE_WRITE,
                      NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
   if( hFile != INVALID_HANDLE_VALUE )
   {
      CloseHandle(hFile);
      return TRUE;
   }
   return FALSE;
}

Тпзи трик вика CreateFileA функцията,но не очаквайте да можете да го преодолеете
като инсталирате IFS hook:нямада проработи!
В действителност след извикването на CreateFileA ще премине през VWIN32 0x001F
service _VWIN32_ReleaseWin32Mutex (през Kernel32!ORD_0001/VxDCall функцията)
и после се отваря DDB листата докато намери VxD и неговото DDB_Control_Proc
поле.
В същтност, не е да зареди/терминира VxDs,а само да изпрати W32_DEVICEIOCONTROL
(0x23) контролно съонщение (DIOC_OPEN и DIOC_CLOSEHANDLE)
към процедурата VxD Control_Dispatch. 
Ако VxD е зареден,eax и флага за пренос ще се нулират за да може да се отвори
неговия handle и той ще бъде засечен.
Можете много просто да проверите това като hooking Winice.exe процедурата за
входна точка,докато Meltice се изпълнява.


  00401067:  push      00402025    ; \\.\SICE
  0040106C:  call      CreateFileA
  00401071:  cmp       eax,-001
  00401074:  je        00401091


Има страшно много точки на прекъсване,който можете да използвате,за да засечете
този трик.
-Най-използваната е:
  BPX CreateFileA if *(esp->4+4)=='SICE' || *(esp->4+4)=='SIWV' ||
    *(esp->4+4)=='NTIC'

-Ето и една по различна (но е доста бавна)
   BPINT 30 if eax==002A001F && (*edi=='SICE' || *edi=='SIWV')  
     ;ще прекъсне 3 пъти

-ето и малко по бърз вариянт: 
   BPINT 30 if (*edi=='SICE' || *edi=='SIWV')

   BPX KERNEL32!ORD_0001 if *edi=='SICE' || *edi=='SIWV'  
     ;пак ще прекъсне 3 пъти 

-ето и друга:
   BPX VMM_GetDDBList if eax->3=='SICE' || eax->3=='SIWV'

Трябва да се отбележи,че някои програми като AZPR3.00 използват старата old 16-битова 
функция _lopen,за да се извърши същото нещо:

   push    00                        ; OF_READ
   mov     eax,[00656634]            ; '\\.\SICE',0
   push    eax
   call    KERNEL32!_lopen
   inc     eax
   jnz     00650589                  ; засечен Softice
   push    00                        ; OF_READ
   mov     eax,[00656638]            ; '\\.\SICE'
   push    eax
   call    KERNEL32!_lopen
   inc     eax
   jz      006505ae                  ; не е засечен Softice


__________________________________________________________________________

Mетод 12
========

Този трик е подобен на дебъгерската инсталационна проверка int41h/4fh (code 05
и 06),но много ограничен,защото е възможен само за Win95/98 (без NT),защото
използва VxDCall backdoor функция.Това засичанее използвано в демото на Bleem.

   push  0000004fh         ; функцията 4fh
   push  002a002ah         ; лявата дума VxD (VWIN32)
                           ; low word specifies which service
                             (VWIN32_Int41Dispatch)
   call  Kernel32!ORD_001  ; VxdCall
   cmp   ax, 0f386h        ; магическия номер,връщан от системните дебъгери
   jz    SoftICE засечен

Ето отново няколко начина,по които може да бъде засечен:

    BPINT 41 if ax==4f

    BPINT 30 if ax==0xF386   ; SoftICE трябва да е зареден

    BPX Exec_PM_Int if eax==41 && edx->1c==4f && edx->10==002A002A

    BPX Kernel32!ord_0001 if esp->4==002A002A && esp->8==4f   

__________________________________________________________________________

Mетод 12
========

Не е метод за засичане на Softice,но добър начин да се провери дали Softice е
инсталиран на този компютър и да се определи неговата директория.
Използва се от програми,които се занимават с регистрите на Windows:

-#1: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion
\Uninstall\SoftICE
-#2: HKEY_LOCAL_MACHINE\Software\NuMega\SoftICE
-#3: HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion
\App Paths\Loader32.Exe


Някои програми могат да изтрията даже всички файлове на Softice и цялата директория.


МНого подходящи точки на прекъсване:

     BPX _regopenkey if *(esp->8+0x13)=='tICE' || *(esp->8+0x37)=='tICE'

__________________________________________________________________________


Mетод 13 
========

Извиква се VMM 'Test_Debug_Installed'.Неговата цел е да определи дали зареден дебъгер
в рама(само в среда ring0)

   VMMCall Test_Debug_Installed
   je      не е инсталиран

Този метод проверява само един флаг.

 

 

Нека да се върнем към нашата основна задача:разопаковането на notepad.exe.Разгледахме защитата BoundsChecker.Като 

промените флага за нулев резултат на адрес:0040D0B7 ще продължи разопаковането на notepad.exe.В противен случай програмата излиза.Обръщам още веднъж внимани,че опцията Boundschacker трябва да е зададена,за да се засече Softice.Иначе не е нужно да се променя нулевия флаг.

Ето и продължението на кода:

yC:0040D0BB call sub_40D269  //в този call се извършват операции с Import Table-a;чете се всяко поле от Import Table-a
yC:0040D0C0 cmp eax, 1  //успешно ли е завършен процеса
yC:0040D0C3 jz short loc_40D0C7  //ако е така продължи
yC:0040D0C5 popa  //възстановява всички регистри 
yC:0040D0C6 retn  //излез                                       
Ако ма адрес 40D0C3 флага за нулев резултат не е установен,трябва ръчно да го направите,защото в противен случай програмата ще излезе.Но преди това е по-важно да премахнете call-функцията на адрес 40D0BB или да го прескочите по някакъв начин.

В call-a на адрес 0040D0BB се извършват много операции.Взимат се последователно DLL-ите,като се чете Import Table-a.За даможете да разберете кода трябва да знаете малко повече за Import Table.Ето и кода който ще видите ако влезете в този call.Но аз ви препоръчвам да не се задълбавате чак толкова и просто да trace-нете над call-a с F8. 

yC:0040D269 mov eax, dword ptr ss:(loc_401F29+3)[ebp]  //eax=CRC
yC:0040D26F or eax, eax  //CRC=0?
yC:0040D271 jz short loc_40D280  //ако е така скочи
yC:0040D273 cmp eax, dword ptr ss:byte_401FC3[ebp]  //сравнява истинското CRC с преди това изчисленото
yC:0040D279 jz short loc_40D280  //равни ли са?;ако да скочи
yC:0040D27B jmp locret_40D34A  //ако не скочи на адрес 40D34A и излез
yC:0040D280 mov esi, ss:dword_401F1C[ebp]  //esi=VirtualOffset на idata(Import Table)
yC:0040D286 add esi, dword ptr ss:(loc_401F12+2)[ebp]  // + Image Base
yC:0040D28C jmp loc_40D33D  //скача на адрес 40D33D |-------------------------
...................................                                                                                         | 

yC:0040D33D cmp dword ptr [esi+10h], 0  //има ли някакъв DLL??      <<-------------                      
yC:0040D341 jnz loc_40D291  //да? тогава скачи          |----------------------
yC:0040D347 xor eax, eax  //иначе eax=0                                                      \
yC:0040D349 inc eax  //eax=1                                                                         \
yC:0040D34A retn  //върни се към главния код                                                /

-----------------------------                                                                                /

yC:0040D291 mov ebx, [esi+0Ch]  //ebx съдържа адреса на DLL    <<--------                                                    yC:0040D294 add ebx, dword ptr ss:(loc_401F12+2)[ebp]  //+ Image Base
yC:0040D29A push ebx  //запазва се стойността на EBX(адреса на DLL-a
yC:0040D29B call dword ptr ss:loc_401F20[ebp]  //в този call се извършват всеки път операции с различни DLL-и
yC:0040D2A1 test eax, eax  //eax=0?;има ли още DLL-и в Import Table-a
yC:0040D2A3 jz locret_40D34A  //ако nqma скочи на 40D34A и излез
yC:0040D2A9 test dword ptr ss:(loc_401F27+1)[ebp], 4  //сравнява стойността на адрес 401F28 с 4
yC:0040D2B3 jz short loc_40D2BB  //равни ли са,ако да скочи |-----------------------------------------------------------
yC:0040D2B5 push ebx  //запазва се адреса на DLL-a в стека                                                                            |
yC:0040D2B6 call sub_40D34B   //в този call се    унищожава името на DLL-a в Import Table-a;за да ви стане по-ясно трябва да прочетете малко за Import Table;има член в ImportTable-a наречен Name-името на DLL-a                                               
yC:0040D2BB mov ebx, eax  //ebx сочи следващия байт след члена  Name  <<--------------------------------</
yC:0040D2BD mov ecx, [esi]  //ecx присвоява стойността,която се сочи от esi
yC:0040D2BF or ecx, ecx  //ecx=0?
yC:0040D2C1 jnz short loc_40D2C6  //ако е така не скачай
yC:0040D2C3 mov ecx, [esi+10h]  //премества адреса на следващия DLL  
yC:0040D2C6 add ecx, dword ptr ss:(loc_401F12+2)[ebp]  //+Image Base 
yC:0040D2CC mov edx, [esi+10h]  //премества адреса  на следващия DLL
yC:0040D2CF add edx, dword ptr ss:(loc_401F12+2)[ebp]  //+Image Base
yC:0040D2D5 jmp short loc_40D335  //скача на адрес 40D335
yC:0040D2D7 test dword ptr [ecx], 80000000h  //стойността сочена от ecx 80000000х ли е?
yC:0040D2DD jnz short loc_40D316  //ако не скочи
yC:0040D2DF mov eax, [ecx]  //eax:=приема адреса сочен от ecx(името на API функцията)
yC:0040D2E1 add eax, 2  //eax:=eax+2;трябва да се прибави 2 са да сочи eax самото име(прочетете за Import Table и ще разберете защо;)),сега не ми се обяснява и за него;)
yC:0040D2E4 add eax, dword ptr ss:(loc_401F12+2)[ebp]  //+Image Base
yC:0040D2EA mov edi, eax  //edi сочи името на функцията
yC:0040D2EC push edx  \\
yC:0040D2ED push ecx    \\  запазват се регистрите,защото ще бъдат използвани в следващия call
yC:0040D2EE push eax    //
yC:0040D2EF push ebx   // 
yC:0040D2F0 call dword ptr ss:byte_401F24[ebp]  //унищожава се името на API fункцията
yC:0040D2F6 or eax, eax  //eax=0?
yC:0040D2F8 jnz short loc_40D2FE  //ако не е равно скочи
yC:0040D2FA pop ecx     \\
yC:0040D2FB pop edx      \\  
yC:0040D2FC jmp 40D34A  >>възстановяват се запазените регистри
yC:0040D2FE pop ecx       //
yC:0040D2FF pop edx     //
yC:0040D300 test dword ptr ss:(loc_401F27+1)[ebp], 4   //сравнява стойността на адрес 401F28 с 4
yC:0040D30A jz short loc_40D312  //ако е равно скочи
yC:0040D30C push edi  //запазва стойността на edi
yC:0040D30D call sub_40D34B  //  в този call не мога да вляза с Olly пък ме мързи да използвам Softice;));пробвайте сами знам само че прави нещо с някаква API оункция
yC:0040D2FC jmp short locret_40D34A  //скочи и излез
yC:0040D312 mov [edx], eax  //eax сочи към API,което се премества на адреса сочен от edx
yC:0040D314 jmp short loc_40D32F  //скача на 40D32F
yC:0040D316 push edx  //запазва се стойността на edx
yC:0040D317 push ecx  //запазва се стойността на ecx
yC:0040D318 mov eax, [ecx]  //стойността сочена от ecx се премества в eax
yC:0040D31A sub eax, 80000000h  //еax:=eax-80000000h
yC:0040D31F push eax  //eax се запазва
yC:0040D320 push ebx  //ebx се запазва
yC:0040D321 call dword ptr ss:byte_401F24[ebp]  //унищожава се името на API функцията
yC:0040D327 test eax, eax  //eax=0?
yC:0040D329 jz short locret_40D34A  //ако да скочи
yC:0040D32B pop ecx  //възвръща се стойността в ecx
yC:0040D32C pop edx  //възвръща се стойността в edx
yC:0040D32D mov [edx], eax  //стойността на eax се премества на адреса сочен от edx
yC:0040D32F add ecx, 4  //ecx:=ecx+4  
yC:0040D332 add edx, 4  //edx:=edx+4
yC:0040D335 cmp dword ptr [ecx], 0  //нулев ли е байта на адреса сочен от ecx;ecx сочи член от Import Table
yC:0040D338 jnz short loc_40D2D7  //ако не скочи
yC:0040D33A add esi, 14h  //следващич DLL
yC:0040D33D cmp dword ptr [esi+10h], 0  //има ли някакъв DLL още
yC:0040D341 jnz loc_40D291  //ако да скочи
yC:0040D347 xor eax, eax  //ако не eax:=0
yC:0040D349 inc eax  //eax:=eax+1
yC:0040D34A retn  //отиди към главния код

 

От тези коментари не можете да разберете какво в действителност става.Но като цяло функцията на този call e да прочете Import Table-a и да замести всички попълнения в Import Table-a с техните основните адреси.За да можете да се ориентирате трябва да знаете много добре Import Table-a,нейната структура и т.н.Това мога да го обясня в някой друг туториал че този и без туй стана достатъчно дълъг.Но ако се интересувате има толкова много източници и материали на тема IT(Import Table).

 Oстанаха още 2 неща за преодоляване:опцията за изтриване на Pe Header-a и Import Table-a.

Ето първата и съответно кода:

yC:0040D0C7 test ss:(off_401F25+3)[ebp], 2  //зададена ли  опциятаза изтриване на Pe Header-a?
yC:0040D0D1 jz short loc_40D0D8  //ако не скочи;трябва да установите нулевия флаг
yC:0040D0D3 call sub_40D22A  //в този call се изтрива Pe Header-a                                                 
За да видим как става това ще влезем в call-a nь адрес 40D0D3.Там се изтрива Pe Header-a.Сигурно се чудите как се разбира че това проверява дали е задействана опцията за изтриване на Pe Header-a.Има 2 начина.Най-добре е като ги приложите и двата.В този случай влизате в call-a,който е след условния скок и проверявате какво става там.По този начин ще видите как се изтрива Pe Header-a и съответно разбиате,че инструкцията test и jz определят дали е зададена тази Опция.И другия начин:опаковате eexe-то наново пак със всички функции,като само махате тази за изтриване на Pe Header-a и така ще разберете дали товае изтриването на Pe Header-a, като видите на адрес 40D0D1 dьли ще се изпълни условния скок.

Ето и самото изтриване:

yC:0040D22A mov edi, dword ptr ss:(loc_401F12+2)[ebp]  //edi:=Image Base
yC:0040D230 add edi, [edi+3Ch]  //edi сочи началото на Pe Header-a
yC:0040D233 mov ecx, [edi+54h]  //размера на header-ите в байтове
yC:0040D236 mov esi, dword ptr ss:(loc_401F12+2)[ebp]  //esi:=Image Base
yC:0040D23C mov byte ptr [esi], 0  //нулира се байта сочен от esi;изтрива се Header-a
yC:0040D23F inc esi  //esi е сочещ регистър,неговата стойност се повишава всеки път с един байт
yC:0040D240 loop loc_40D23C  //изпълнява се цикъла,докато ecx(SiezeOfHeaders)=0;докато целия header е изтрит
yC:0040D242 retn  //връща се към главния код
Надявам се,че по-горните коментари са достатъчно ясни.Сигурно и без тях можете да се справите,защото кода е съвсем ясен.
Сега да преминем към следващата:изтриването на Import Table.

yC:0040D0D8 test ss:(off_401F25+3)[ebp], 4  //зададена ли е опцията за изтриване на Import Table  
yC:0040D0E2 jz short loc_40D0F5  //ако не скочи ;трябва да установите нулевия флаг
yC:0040D0E4 mov eax, ss:dword_401F1C[ebp]  //eax:=VirtualOffset на idata
yC:0040D0EA add eax, dword ptr ss:(loc_401F12+2)[ebp]  //+Image Base
yC:0040D0F0 call sub_40D243  //в този call се изтрива Import Table-a
По същия начин както миналия път може да проверите дали това е наистина проверката за опцията за изтриване на IT.Сега пак трябва да влезем в call-a за да разберем как се изтрива Import Table-a.Трябва да запомните стойността на eax на адрес 40D0E4.Toй ще ви трябва когато поправяте Pe-Header-a след dump-ването.(Стойността на този адрес е 6000)

Ето кода,който трябва да видите ако влезете в самия call.

yC:0040D243 pusha  //всички регистри се запазват,защото ще бъдат използвани в следващия код
yC:0040D244 mov esi, eax  //esi:=VirtualOffset на idata(Import Table)
yC:0040D246 mov edi, esi  //edi:=VirtualOffset на idata(началото на тази секция)
yC:0040D248 xor eax, eax    //eax=0
yC:0040D24A jmp short loc_40D250  //скача на адрес 40D250
yC:0040D24C inc eax  //еаx:=eax+1
yC:0040D24D add esi, 14h  //esi сочи следващия DLL
yC:0040D250 cmp dword ptr [esi+10h], 0  //има ли някакъв DLL?
yC:0040D254 jnz short loc_40D24C  //ако да скочи
yC:0040D256 inc eax  //еаx:=eax+1;eax съдържа броя на DLL-ите
yC:0040D257 xor edx, edx  //edx=0
yC:0040D259 mov ebx, 14h  //ebx=14
yC:0040D25E mul ebx  //eax съдържа размера на Import Table-a 
yC:0040D260 xchg eax, ecx  //eax:=ecx и ecx:=eax;ecx съдържа размера на Import Table-a
yC:0040D261 mov byte ptr [edi], 0  //унищожава Import Table-a
yC:0040D264 inc edi  //update-ва всеки път edi,който сочи поредния байт
yC:0040D265 loop loc_40D261 //повтаряй докато ecx=0
yC:0040D267 popa  //връща всички регистри
yC:0040D268 retn  //излиза
От по-горните коментари можете да разберете как се унищожава Import Table-a.Просто на мястото на Import Table-а се запълва цялото пространство с нули.

Сега е време да пристъпим към последната фаза,където няма преодоляване на защити.Ще намерим скока към оригиналната входна точка и ще запишем еxe-то директно от рама на твърдия диск.

Ето и последната част от кода:

yC:0040D0F5 mov eax, dword ptr ss:(loc_401F16+2)[ebp]  //eax=OEP
yC:0040D0FB add eax, dword ptr ss:(loc_401F12+2)[ebp]  //+Image Base
yC:0040D101 mov ebx, offset loc_401B8D  //ebx:=401B8D              ==\\          Както си спомняте в началото взехме 
yC:0040D106 add ebx, ebp  // ebx:=ebx+ebp                                   ===   >>    Delta Offset-a.Тези команди възвръщат
yC:0040D108 inc ebx  //ebx+1                                                           ==//          първоначалната форма на регистъра ebp
yC:0040D109 mov [ebx], eax  //eax=OEP
yC:0040D10B popa  //всички регистри се възстановяват
yC:0040D10C mov eax, 0FFFFFFFFh  //еаx=истинската входна точка
yC:0040D111 jmp eax  //скача към истинската входна точка

Запишете си стойността на eax на адрес 0040D111.В този случай тя трябва да е 4010CC.Сега трябва да dump-нем exe-то с ProcDump или LordPe.След dump-ването просто сменете входната точка от D000 на 10CC(за входната точка ни трябва само соновния адрес,който не включва Image Base)Сега трябва да поправим и RVA-то на Import Table-a.Надявам се че помните Virtual Offset-a на Import Table-a.Той е 6000.Заменете предишното RVA на Import Table-a(за всички поправки използвайте Pe Editor) D000 с 6000.

И готово exe-то работи!Като го дисасемблирате ще можете да видите истинския код на програмата и нейните ресурси.

Последни думи:

 

 Нямам представа тука какво трябва да пиша.Може би нещо като обобщение;).Надявам се този туториал да ви е бил полезен.Писал съм за много други неща не само за самото разопаковане.Този тут можеше да стане много по-кратък,но този път реших да беде нещо по-различно.Надявам се че ако не ви е харесало,поне не ви е отегчило;))

 

Доста е дълъг,а?;))


-------------------------------------------------------------------------------------------------------------------------------------------

 

Int3-метод

Ако не сте запознати с този начин на прекъсване на входната точка на пакера прочетете следните редове.

Условието в началото беше,че имаме Softice и HexEditor,и евентуално някакъв Pe Editor.Казвам евентуално,защото Pe Editor-a служи за улеснение,но ако можете да се оправяте и без Pe Editor е още по добре.

Аз лично ще работя без Pe Editor за да стане по интересно изследването.Много важно нещото което трябва да го знаете и да се съобразявате с него е,че всички стойности,който виждате и ще видите след малко с вашия Hex Editor са записани в обратен ред.

Първата стъпка която трябва да изпълним е да намерим Offset-a на входната точка на програмата.След като знаеме,че в Optional Header-a имаме член наречен AddressOfEntryPoint,където е записано RVA-то(VA-ImageBase),където трябва да започва изпълнението на програмата.Този член е 7-мия от Optional Header.Вижте в winnt.h какви са членовете преди AddressOfEntryPoint и колко са дълги те,а ако ви мързи просто ми се доверете и вижте следната информация която аз ви давам.Това са стандарти така че не е нужно да ги знаете наизуст всичките а можете просто да ги проверявате. 

->Optional Header

Magic: 0x010B (HDR32_MAGIC)    //1 word

MajorLinkerVersion: 0x03             //1 byte

MinorLinkerVersion: 0x0A -> 3.10   //1 byte

SizeOfCode: 0x00004000               //1 dword

SizeOfInitializedData: 0x00007400  //1 dword

SizeOfUninitializedData: 0x00000000  //1 dword

AddressOfEntryPoint: 0x0000D060    //1 dword

 

За да намерим в Hex Editor-a къде се намира този член AddressOfEntryPoint трябва да намерим началото на Optional Header-a и да прибавим байтовете до този член.Съберете дължината на  всички предходни членове : Magic+MajorLinkerVersion+MinorLinkerVersion+SizeOfCode+SizeOfInitializedData+SizeOfUninitializedData=16 байта.Значи след като намерим началото на Optional Header-a ще съберем 12 байта и ще стигнем до нашата цел.Сега въпросат който възниква е как можем да намерим началото на Optional Header-a.Можем да го разпознаем много лесно като търсим за неговия  първи член magic,който винаги съдържа стойността 010B .Затова потърсете във вашия Hex Editor тази стойност,но в обратен ред:0B01 .Като намерите на кой offset се намира тази стойност прибавете 16:98+16=АE>трябва да видите следната стойност:60D00000-->това е RVA-то на което трябва да започне изпълнението на програмата.Но това е RVA=>адрес в паметта а не на твърдия диск.За да намерим входната точка на програмата в Hex Editor-a  трябва първо да определим къмкоя секция принадлежи входната точка,тоест от коя секция започва изпълнението на кода.Съобразявайки се, че по принцип SectionAligment e 1000,a FileAlignment - 200 ще намерим към коя секция трябва да се причисли входната точка.Нека да разгледаме Section Header-a на този файл.Но как да стигнем до него?От Dos-Header-a разбираме къде започва Pe Header-a като гледаме полето e_lfanew:00000080 (в паметта)=>Pe Header-a започва от offset 80,прибавяме към него размера на File Header-a -18//бройте байтовете от началото на Pe Header докато стигнете 0B01-магическа дума за Optional Header//(със magic word-PE) и се озоваваме в началото на Optional header-a,което е на offset 98.Сега няма нужда да изчисляваме неговия размер като бройм байтовете ред по ред както направихме с File Header-a,a просто погледнете полето в File Header SizeOfOptionalHeader,което съдържа е E0 и като знаем че Section Table започва след края на Pe Header(включително и Data Directories) просто прибавяме към на offset-a,от където започва Optional Header-a,а именно 98 , еазмера на Optional header-a-E0 и като резултат получаваме,че началото на Section Table е 98+E0=178.От полето NumberOfSections в File Header разбираме броя на секциите,а те са 6=> в Section Table ще имаме 6 Header-a.Ето и структурата на всеки Section Header- за всяка една секция от нашето exe секця(стойностите са в обратен ред във вашия Hex Editor):

   1)                                            2)                                  3)

Name:.text                                        Name:.data                                 Name:.idata

VirtualAddress:1000                           VirtualAddress:5000                    VirtualAddress:6000

VirtualSize:3E9C                                VirtualSize:84C                           VirtualSize:DE8

RawOffset:1000                                 RawOffset:5000                           RawOffset:6000

RawSize:4000                                    RawSize:1000                             RawSize:1000

Flags:E0000020                                 Flags:C0000040                           Flags:C0000040 

   4)                                            5)                                  6)

Name:.rsrc                                        Name:.reloc                                Name:yC

VirtualAddress:7000                           VirtualAddress:C000                    VirtualAddress:D000

VirtualSize:4FB8                                VirtualSize:A9C                           VirtualSize:1000

RawOffset:7000                                 RawOffset:C000                           RawOffset:D000

RawSize:5000                                    RawSize:1000                             RawSize:547

Flags:C0000040                                 Flags:42000040                           Flags:E00000E0       

Ето и как изглеждат Section Header-ите на тези секции в Hex Editor-a:    //маркираното с черно е Section Header-a

1)

      

2)

3)

   

4)

5)

6)

 

Ако се вгледате по внимателно ще забележите че местата на VirtualSize и VirtualAddress,и на RawSize и RawOffset са разменени-тоест с вашия Pe Editor вие виждате,че първо е представен VirtualAddress,a после VirtualSize а в Hex Editor-a е в обратен ред.Същото важи и за RawSize и RawOffset.

Нека да се върнем към нашата цел за да намерим към коя секция принадлежи входящата точка ще сравняваме на всеки Section Header стойността  в полето VirtualAddress с стойността от полето AddressOfEntryPoint,като трябва да спазваме правилото:входната точка трябва да е между началото на секцията и нейния край(VirtualAddress<EntryPoint<VirtualAddress+VirtualSize).Нашата входна точка е D060.Ето и сравняването:

1) 

.text:    1000<D060<1000+3E9C  //не е изпълнено

2)

.data:   5000<D060<5000+84C   //не е изпълнено

3)

.idata:  6000<D060<6000+DE8   //не е изпълнено

4)

.rsrc:    7000<D060<7000+4FB8  //не е изпълнено

5)

.reloc:   C000<D060<C000+A9C  //не е изпълнено

6)

yC:       D000<D060<D000+1000  //изпълнено е => входната точка принадлежи към тази секция

Сега за да намерим Offset-a от който започва изпълнението на програмата трябва да извадим от входната точка VirtualAddress на секзията,към която е входната точка-тоест последната yC секция.После към полученото число трябва да прибавим и RawOffset на същата секция.Така вече ще знаем offset-a,от който започва изпълнението на програмата.

EntryPoint-VirtualAddress+RawOffset  => D060-D000+D000=D060 

Offset-ът,който получихме съвпада с стойността на AddressOfEntryPoint.Сега излиза че вскичко,което сме правили до момента е напразно.В този случай,да!Но в повечето случаи тези две стойности няма да са равни.Тука се получава така,защото стойностите на SectionAlignment и FileAlignment са равни(SectionAlignment=FileAlignment).Когато видите че тези стойности са равни можете да си спестите тези изчислявания и просто да приемете стойността на полето AddressOfEntryPoint като Offset.

Намерихме адреса:D060 =>отидете на този Offset в Hex Editor-a си и би трябвало да видите тези първоначални байтове:

60 E8 00 00 => за да можем да спрем изпълнението на програмата трябва да сложим точка на прекъсване на първия байт-тоест на мястото на 60.На мястото на 60 трябва да сложим CC.Всъщтност заменяме PUSHAD с INT3.Запаметете промяната и излезте от Hex Editor-a.

Сега вече сме готови да прекъснем изпълнението на програмата.Стартирайте Softice напишете int3 и после <enter>.Стартирайте exe-то и то трябва да спре на входната точка.

      Забележка:Не забравяйте да възстановите стария код като напишете е eip 60.


-----------------------------------------------------------------------------------------------------------------------------------------------------------
Greetingz to

 

---Shade-----
---plux-------
---Dim_cr----
---Pumqara--
---
Buko--------
--всеки,който имаше търпението да този туториал прочете докрая---------

---------------------------------------------------------------------------------------------------------------------------------------------:stan4oo:---

Дата: 19.01.2оо4

Автор: stan4oo!prf

Поща: stanko_popov@abv.bg